﻿using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Xml.Serialization;
using System.Xml.XPath;
using System.Xml.Xsl;

namespace SharedLib.Helpers
{
    public class XmlHelper
    {
        #region Conversion between Object and XML
        public static XmlDocument ObjectToXml(object obj, Encoding encoding, bool indent, XmlSerializerNamespaces ns)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(ObjectToXmlText(obj, encoding, indent, ns));
            return doc;
        }

        public static string ObjectToXmlText(object obj, Encoding encoding, bool indent, XmlSerializerNamespaces ns)
        {
            if (obj == null)
            {
                return null;
            }
            MemoryStream ms = new MemoryStream();
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = encoding;
            settings.Indent = indent;
            XmlWriter xtw = XmlWriter.Create(ms, settings);

            string rtn = null;
            try
            {
                XmlSerializer serializer = new XmlSerializer(obj.GetType());
                serializer.Serialize(xtw, obj, ns);
                rtn = encoding.GetString(ms.ToArray());
            }
            finally
            {
                xtw.Close();
            }
            return rtn;
        }

        public static XmlDocument ObjectToXml(object obj)
        {
            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add("", "");
            return ObjectToXml(obj, new UTF8Encoding(false), false, ns);
        }

        public static string ObjectToXmlText(object obj)
        {
            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add("", "");
            return ObjectToXmlText(obj, new UTF8Encoding(false), false, ns);
        }

        public static object XmlTextToObject(string XmlText, Type type)
        {
            object rtn = null;
            StringReader sr = new StringReader(XmlText);
            XmlTextReader xtr = new XmlTextReader(sr);
            try
            {
                XmlSerializer serializer = new XmlSerializer(type);
                if (serializer.CanDeserialize(xtr))
                {
                    rtn = serializer.Deserialize(xtr);
                }
            }
            finally
            {
                sr.Close();
            }

            return rtn;
        }

        public static object XmlToObject(XmlNode doc, Type type)
        {
            return XmlTextToObject(doc.OuterXml, type);
        }

        #endregion

        #region "XML operations"

        public static XmlDocument LoadXml(string xmlFilePath)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(xmlFilePath);
            return doc;
        }

        public static XmlDocument LoadXmlText(string xmlText)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xmlText);
            return doc;
        }

        public static List<string> GetNodeValues(XmlNode node, string xpath)
        {
            List<string> rtn = new List<string>();
            if (node == null)
            {
                return rtn;
            }
            
            foreach (XmlNode result in node.SelectNodes(xpath))
            {
                rtn.Add(GetNodeValue(result));
            }
            return rtn;
        }

        public static string GetNodeValue(XmlNode node, string xpath)
        {
            if (node == null)
            {
                return null;
            }
            return GetNodeValue(node.SelectSingleNode(xpath));
        }

        public static string GetNodeValue(XmlNode node)
        {
            if (node == null)
            {
                return null;
            }
            else
            {
                if (node is XmlElement)
                {
                    //Get Value of XmlText / CData Section under this XmlElement
                    foreach (XmlNode childNode in node.ChildNodes)
                    {
                        if (childNode is XmlElement)
                        {
                            return null;
                        }
                        else if (childNode is XmlText)
                        {
                            return childNode.Value;
                        }
                        else if (childNode is XmlCDataSection)
                        {
                            return childNode.Value;
                        }
                    }
                    return null;
                }
                else
                {
                    return node.Value;
                }
            }
        }

        public static bool SetNodeValue(XmlNode node, string xpath, string value)
        {
            if (node == null)
            {
                return false;
            }
            return SetNodeValue(node.SelectSingleNode(xpath), value);
        }

        public static bool SetNodeValue(XmlNode node, string value)
        {
            if (node == null)
            {
                return false;
            }
            else
            {
                if (node is XmlElement)
                {
                    //Get Value of XmlText / CData Section under this XmlElement
                    foreach (XmlNode childNode in node.ChildNodes)
                    {
                        if (childNode is XmlElement)
                        {
                            return false;
                        }
                        else if (childNode is XmlText)
                        {
                            childNode.Value = value;
                            return true;
                        }
                        else if (childNode is XmlCDataSection)
                        {
                            childNode.Value = value;
                            return true;
                        }
                    }
                    return false;
                }
                else
                {
                    node.Value = value;
                    return true;
                }
            }
        }

        public static XmlElement CreateElementWithTextValue(string elementName, string textValue, XmlDocument doc)
        {
            XmlElement node = doc.CreateElement(elementName);
            node.AppendChild(doc.CreateTextNode(textValue));
            return node;
        }

        public static XmlElement GetChildElement(XmlNode parentNode)
        {
            foreach (XmlNode node in parentNode.ChildNodes)
            {
                if (node is XmlElement)
                {
                    return (XmlElement)node;
                }
            }
            return null;
        }

        public static XmlElement GetChildElement(XmlNode parentNode, string childNodeName, bool createIfNotExists)
        {
            if (parentNode == null)
            {
                return null;
            }
            XmlDocument doc = GetDocument(parentNode);

            XmlNode childNode = parentNode.SelectSingleNode(childNodeName);
            if (childNode != null)
            {
                if (childNode is XmlElement)
                {
                    return (XmlElement)childNode;
                }
            }
            else if (createIfNotExists && doc != null)
            {
                return (XmlElement)parentNode.AppendChild(doc.CreateElement(childNodeName));
            }
            return null;
        }

        public static XmlDocument GetDocument(XmlNode node)
        {
            if (node is XmlDocument)
            {
                return (XmlDocument)node;
            }
            else
            {
                return node.OwnerDocument;
            }
        }




        public static XmlNode InsertXmlNode(XmlNode newNode, XmlNode parentNode)
        {
            return InsertXmlNode(newNode, parentNode, true);
        }

        public static XmlNode InsertXmlNode(XmlNode newNode, XmlNode parentNode, bool cloneNode)
        {
            XmlNode rtn = null;
            if (newNode != null && parentNode != null)
            {
                if (newNode is XmlDocument)
                {
                    newNode = XmlHelper.GetChildElement((XmlDocument)newNode);
                }
                if (newNode != null)
                {
                    XmlDocument doc = XmlHelper.GetDocument(parentNode);
                    if (!object.ReferenceEquals(XmlHelper.GetDocument(newNode), doc))
                    {
                        //Import if owner document is not same
                        newNode = doc.ImportNode(newNode, true);
                        if (newNode == null)
                        {
                            return null;
                        }
                    }
                    else if (cloneNode)
                    {
                        newNode = newNode.CloneNode(true);
                    }
                    //Insert newNode to parentNode
                    rtn = parentNode.AppendChild(newNode);
                }
            }
            return rtn;
        }

        public static XmlNode UpdateXmlNode(XmlNode newNode, XmlNode oldNode)
        {
            return UpdateXmlNode(newNode, oldNode, true);
        }
        public static XmlNode UpdateXmlNode(XmlNode newNode, XmlNode oldNode, bool cloneNode)
        {
            XmlNode rtn = null;
            if (newNode != null && oldNode != null && oldNode.ParentNode != null)
            {
                if (newNode is XmlDocument)
                {
                    newNode = XmlHelper.GetChildElement((XmlDocument)newNode);
                }
                if (newNode != null)
                {
                    XmlDocument doc = XmlHelper.GetDocument(oldNode);
                    if (!object.ReferenceEquals(XmlHelper.GetDocument(newNode), doc))
                    {
                        //Import if owner document is not same
                        newNode = doc.ImportNode(newNode, true);
                        if (newNode == null)
                        {
                            return null;
                        }
                    }
                    else if (cloneNode)
                    {
                        newNode = newNode.CloneNode(true);
                    }
                    //Replace oldNode with newNode
                    rtn = oldNode.ParentNode.ReplaceChild(newNode, oldNode);
                }
            }
            return rtn;
        }

        public static XmlNode UpdateXmlNode(XmlNode newNode, XmlNode parentNode, string childNodeName)
        {
            XmlNode oldNode = parentNode.SelectSingleNode(childNodeName);
            if (oldNode != null)
            {
                return UpdateXmlNode(newNode, oldNode);
            }
            else
            {
                return InsertXmlNode(newNode, parentNode);
            }
        }

        

        public static string GenerateXPathLocation(XmlNode node)
        {
            return GenerateXPathLocation(node, null);
        }

        public static string GenerateXPathLocation(XmlNode node, string containerNodeName)
        {
            if (node == null)
            {
                throw new ArgumentNullException("node");
            }
            if (node is XmlDocument)
            {
                return "/";
            }
            else if (node is XmlElement)
            {
                XmlNode tmpNode = (XmlNode)node;
                StringBuilder sb = new StringBuilder();
                XmlNode parentNode = null;
                int index = 1;
                while (node.Name != containerNodeName && node.ParentNode != null)
                {
                    parentNode = node.ParentNode;
                    index = GetNodeIndex(parentNode.SelectNodes(node.Name), node) + 1;
                    if (index > 1)
                    {
                        sb.Insert(0, "]");
                        sb.Insert(0, index);
                        sb.Insert(0, "[");
                    }
                    sb.Insert(0, node.Name);
                    sb.Insert(0, "/");
                    node = parentNode;
                }
                return sb.ToString();
            }
            else
            {
                throw new ArgumentException("Input node type '" + node.GetType().Name + "' not supported!", "node");
            }
        }

        public static int GetNodeIndex(XmlNodeList nodeList, XmlNode node)
        {
            if (nodeList != null && node != null)
            {
                int index = -1;
                foreach (XmlNode n in nodeList)
                {
                    index += 1;
                    if (object.ReferenceEquals(n, node))
                    {
                        return index;
                    }
                }
            }
            return -1;
        }

        public static XmlDocument PerformXslTrasform(XmlDocument source, XslCompiledTransform xslt)
        {
            XmlDocument rtn;
            StringWriter sw = new StringWriter();
            XmlTextWriter xtw = new XmlTextWriter(sw);
            try
            {
                xslt.Transform(source, xtw);
                rtn = new XmlDocument();
                rtn.LoadXml(sw.ToString());

            }
            finally
            {
                xtw.Close();
                sw.Close();
            }
            return rtn;
        }
        #endregion
    }
}